package com.sunricher.telinkblemeshlib.models;

import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.sunricher.telinkblemeshlib.MeshCommand;

import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import androidx.annotation.Nullable;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import androidx.room.TypeConverters;

@Entity(tableName = "room_scenes")
@TypeConverters(RoomSceneActionConverters.class)
public class RoomScene implements Serializable {

    private static final Gson gson = new Gson();

    public static class Action implements Serializable {
        public int target;
        public Boolean isOn;
        public Integer brightness;
        public Integer white;
        public Integer colorTemperature;
        public Integer rgb;

        public Action(int target) {
            this.target = target;
        }

        public Map<String, Object> getJson() {
            Map<String, Object> result = new HashMap<>();
            result.put("target", target);
            if (isOn != null) result.put("isOn", isOn);
            if (brightness != null) result.put("brightness", brightness);
            if (white != null) result.put("white", white);
            if (colorTemperature != null) result.put("colorTemperature", colorTemperature);
            if (rgb != null) result.put("rgb", rgb);
            return result;
        }

        public static Action makeActionFromJson(Map<String, Object> json) {
            if (!json.containsKey("target") || !(json.get("target") instanceof Number)) {
                return null;
            }
            Action action = new Action(((Number) Objects.requireNonNull(json.get("target"))).intValue());
            if (json.containsKey("isOn")) action.isOn = (Boolean) json.get("isOn");
            if (json.containsKey("brightness"))
                action.brightness = ((Number) Objects.requireNonNull(json.get("brightness"))).intValue();
            if (json.containsKey("white"))
                action.white = ((Number) Objects.requireNonNull(json.get("white"))).intValue();
            if (json.containsKey("colorTemperature"))
                action.colorTemperature = ((Number) Objects.requireNonNull(json.get("colorTemperature"))).intValue();
            if (json.containsKey("rgb"))
                action.rgb = ((Number) Objects.requireNonNull(json.get("rgb"))).intValue();
            return action;
        }

        public List<MeshCommand> getCommands() {
            List<MeshCommand> commands = new ArrayList<>();

            if (rgb != null) {
                int red = (rgb >> 16) & 0xFF;
                int green = (rgb >> 8) & 0xFF;
                int blue = rgb & 0xFF;
                commands.add(MeshCommand.setRgb(target, red, green, blue));
            }

            if (isOn != null) {
                commands.add(MeshCommand.turnOnOff(target, isOn));
            }

            if (colorTemperature != null) {
                commands.add(MeshCommand.setColorTemperature(target, colorTemperature));
            }

            if (white != null) {
                commands.add(MeshCommand.setWhitePercentage(target, white));
            }

            if (brightness != null) {
                commands.add(MeshCommand.setBrightness(target, brightness));
            }

            return commands;
        }
    }

    @PrimaryKey(autoGenerate = true)
    public long identifier = 0;
    public String name;
    public int roomId;
    public List<Action> actions = new ArrayList<>();

    public RoomScene(String name, int roomId) {
        this.name = name;
        this.roomId = roomId;
    }

    public boolean isNotEmpty() {
        return !name.isEmpty() && !actions.isEmpty();
    }

    public boolean containsAllLights() {
        for (Action action : actions) {
            if ((action.target & 0x8000) > 0) {
                return true;
            }
        }
        return false;
    }

    public String getActionsJsonString() {
        try {
            List<Map<String, Object>> actionJsonList = new ArrayList<>();
            for (Action action : actions) {
                actionJsonList.add(action.getJson());
            }
            return gson.toJson(actionJsonList);
        } catch (Exception e) {
            Log.e("RoomScene", "actionsJsonString error: " + e.getMessage());
        }
        return "[]";
    }

    public void updateActionsWithJsonString(String jsonString) {
        try {
            Type listType = new TypeToken<List<Map<String, Object>>>() {
            }.getType();
            List<Map<String, Object>> actionJsonList = gson.fromJson(jsonString, listType);
            actions.clear();
            for (Map<String, Object> actionJson : actionJsonList) {
                Action action = Action.makeActionFromJson(actionJson);
                if (action != null) {
                    actions.add(action);
                }
            }
        } catch (Exception e) {
            Log.e("RoomScene", "updateActionsWithJsonString error: " + e.getMessage());
            actions.clear();
        }
    }

    public List<MeshCommand> getExecuteCommands() {
        List<MeshCommand> commands = new ArrayList<>();
        for (Action action : actions) {
            commands.addAll(action.getCommands());
        }
        return commands;
    }

    public String getJsonString() {
        List<Map<String, Object>> actionsJsonList = new ArrayList<>();
        for (Action action : actions) {
            actionsJsonList.add(action.getJson());
        }
        Map<String, Object> json = new HashMap<>();
        json.put("identifier", identifier);
        json.put("name", name);
        json.put("roomId", roomId);
        json.put("actions", actionsJsonList);
        try {
            return gson.toJson(json);
        } catch (Exception e) {
            Log.e("RoomScene", "getJsonString error: " + e.getMessage());
        }
        return "{}";
    }

    public static RoomScene makeWithJsonString(String jsonString) {
        try {
            Gson gson = new Gson();
            Map<String, Object> json = gson.fromJson(jsonString, new TypeToken<Map<String, Object>>() {
            }.getType());

            if (!json.containsKey("identifier") || !json.containsKey("name") ||
                    !json.containsKey("roomId") || !json.containsKey("actions")) {
                return null;
            }

            long identifier = ((Double) Objects.requireNonNull(json.get("identifier"))).longValue();
            String name = (String) json.get("name");
            int roomId = ((Double) Objects.requireNonNull(json.get("roomId"))).intValue();

            List<Action> actions = new ArrayList<>();
            List<Map<String, Object>> actionsJson = (List<Map<String, Object>>) json.get("actions");

            for (Map<String, Object> actionJson : actionsJson) {
                Action action = Action.makeActionFromJson(actionJson);
                if (action != null) {
                    actions.add(action);
                }
            }

            RoomScene roomScene = new RoomScene(name, roomId);
            roomScene.identifier = identifier;
            roomScene.actions = actions;
            return roomScene;
        } catch (Exception e) {
            Log.e("RoomScene", "JSON parsing error: " + e.getMessage());
        }
        return null;
    }
}